﻿using StructuredProgrammingAssignment.App_Start;
using StructuredProgrammingAssignment.Helpers;
using StructuredProgrammingAssignment.Linq;
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Linq.Dynamic;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace StructuredProgrammingAssignment.Controllers
{
	public class UserOrderController : SPApiController
	{
		/**
		 * Get list of all orders sent to a sepcific user account.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] GetOrders(
			string userId,						// current user id, used for authorization

			string where = null,				// dynamic filter clause, this feature can be turn off via config
			string order = null,				// dynamic order clause, this feature can be turn off via config

			int status = int.MinValue,			// filter stuffs by status
			int paymentType = int.MinValue,		// filter stuffs by payment type

			DateTime? modifiedFrom = null,		// set minimum modified datetime
			DateTime? modifiedTo = null,		// set maximum modified datetime
			DateTime? createdFrom = null,		// set minimum created datetime
			DateTime? createdTo = null,			// set maximum created datetime

			decimal priceMin = decimal.MinValue,	// set minimum price
			decimal priceMax = decimal.MaxValue,	// set maximum price
			bool priceExclusive = true,			// exclusive or not

			string orderCol = "Id",				// order column
			string orderDir = "DESC",			// order direction
			int offset = 0,						// specifies the number of rows to skip
			int limit = 0						// the number of rows to be retrieved
		) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId;
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Double check
				UserAccount account = (from a in db.UserAccounts
									   where a.Id.Equals(parsedUserId)
									   select a).FirstOrDefault();
				if (account == null) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Retrieve list of orders
				var userOrders = (from o in db.Orders
								  where o.UserStuff.UserAccount.Id.Equals(parsedUserId)
								 select o).ToList().AsQueryable();

				// where
				if (where != null) {
					if (!SPConfig.Settings.AllowDynamicQuery)
						return FailureResult("Dynamic query is not allowed.", "dynamic query is not allowed");

					int whereMaxLength = Convert.ToInt32(SPConfig.Settings.DynamicQuery.ValueDictionary()["whereMaxLength"]);

					if (where.Length > whereMaxLength)
						return FailureResult("Dynamic where clause is too long.", "dynamic where clause too long");

					userOrders = userOrders.Where(where);
				}

				// order
				if (order != null) {
					if (!SPConfig.Settings.AllowDynamicQuery)
						return FailureResult("Dynamic query is not allowed.", "dynamic query is not allowed");

					int orderMaxLength = Convert.ToInt32(SPConfig.Settings.DynamicQuery.ValueDictionary()["orderMaxLength"]);

					if (order.Length > orderMaxLength)
						return FailureResult("Dynamic order clause is too long.", "dynamic order clause too long");

				}

				// order
				if (order == null) {
					userOrders = userOrders.OrderBy(String.Format("{0} {1}", orderCol, orderDir));
				} else {
					userOrders = userOrders.OrderBy(String.Format("{0}, {1} {2}", order, orderCol, orderDir));
				}

				// status
				if (status != int.MinValue) {
					userOrders = (from o in userOrders
								  where o.Status == status
								  select o);
				}

				// payment type
				if (paymentType != int.MinValue) {
					userOrders = (from o in userOrders
								  where o.PaymentType == paymentType
								  select o);
				}

				// modified time
				modifiedFrom = modifiedFrom ?? SqlDateTime.MinValue.Value;
				modifiedTo = modifiedTo ?? SqlDateTime.MaxValue.Value;
				userOrders = (from o in userOrders
							  where o.ModifiedDateTime >= modifiedFrom && o.ModifiedDateTime <= modifiedTo
							  select o);

				// created time
				createdFrom = createdFrom ?? SqlDateTime.MinValue.Value;
				createdTo = createdTo ?? SqlDateTime.MaxValue.Value;
				userOrders = (from o in userOrders
							  where o.CreatedDateTime >= createdFrom && o.CreatedDateTime <= createdTo
							  select o);

				// price
				if (priceExclusive) {
					userOrders = (from o in userOrders
								  where o.OfferMoney > priceMin && o.OfferMoney < priceMax
								  select o);
				} else {
					userOrders = (from o in userOrders
								  where o.OfferMoney >= priceMin && o.OfferMoney <= priceMax
								  select o);
				}				

				// offset
				userOrders = userOrders.Skip(offset);

				// limit
				if (limit > 0) {
					userOrders = userOrders.Take(limit);
				}

				// Return list of stuffs
				List<Object> returnObjs = new List<Object>();
				foreach (Order o in userOrders) {
					returnObjs.Add(SPHelper.JsonOrder(o));
				}
				if (returnObjs.Count == 0) returnObjs = null;

				return SuccessResult(returnObjs);
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // GetOrders

		/**
		 * Get list of all orders sent by a sepcific user account.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] GetSentOrders(
			string userId,						// current user id, used for authorization

			string where = null,				// dynamic filter clause, this feature can be turn off via config
			string order = null,				// dynamic order clause, this feature can be turn off via config

			int status = int.MinValue,			// filter stuffs by status
			int paymentType = int.MinValue,		// filter stuffs by payment type

			DateTime? modifiedFrom = null,		// set minimum modified datetime
			DateTime? modifiedTo = null,		// set maximum modified datetime
			DateTime? createdFrom = null,		// set minimum created datetime
			DateTime? createdTo = null,			// set maximum created datetime

			decimal priceMin = decimal.MinValue,	// set minimum price
			decimal priceMax = decimal.MaxValue,	// set maximum price
			bool priceExclusive = true,			// exclusive or not

			string orderCol = "Id",				// order column
			string orderDir = "DESC",			// order direction
			int offset = 0,						// specifies the number of rows to skip
			int limit = 0						// the number of rows to be retrieved
		) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId;
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Double check
				UserAccount account = (from a in db.UserAccounts
									   where a.Id.Equals(parsedUserId)
									   select a).FirstOrDefault();
				if (account == null) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Retrieve list of orders
				var userOrders = (from o in db.Orders
								  where o.UserOrderId.Equals(parsedUserId)
								  select o).ToList().AsQueryable();

				// where
				if (where != null) {
					if (!SPConfig.Settings.AllowDynamicQuery)
						return FailureResult("Dynamic query is not allowed.", "dynamic query is not allowed");

					int whereMaxLength = Convert.ToInt32(SPConfig.Settings.DynamicQuery.ValueDictionary()["whereMaxLength"]);

					if (where.Length > whereMaxLength)
						return FailureResult("Dynamic where clause is too long.", "dynamic where clause too long");

					userOrders = userOrders.Where(where);
				}

				// order
				if (order != null) {
					if (!SPConfig.Settings.AllowDynamicQuery)
						return FailureResult("Dynamic query is not allowed.", "dynamic query is not allowed");

					int orderMaxLength = Convert.ToInt32(SPConfig.Settings.DynamicQuery.ValueDictionary()["orderMaxLength"]);

					if (order.Length > orderMaxLength)
						return FailureResult("Dynamic order clause is too long.", "dynamic order clause too long");

				}

				// order
				if (order == null) {
					userOrders = userOrders.OrderBy(String.Format("{0} {1}", orderCol, orderDir));
				} else {
					userOrders = userOrders.OrderBy(String.Format("{0}, {1} {2}", order, orderCol, orderDir));
				}

				// status
				if (status != int.MinValue) {
					userOrders = (from o in userOrders
								  where o.Status == status
								  select o);
				}

				// payment type
				if (paymentType != int.MinValue) {
					userOrders = (from o in userOrders
								  where o.PaymentType == paymentType
								  select o);
				}

				// modified time
				modifiedFrom = modifiedFrom ?? SqlDateTime.MinValue.Value;
				modifiedTo = modifiedTo ?? SqlDateTime.MaxValue.Value;
				userOrders = (from o in userOrders
							  where o.ModifiedDateTime >= modifiedFrom && o.ModifiedDateTime <= modifiedTo
							  select o);

				// created time
				createdFrom = createdFrom ?? SqlDateTime.MinValue.Value;
				createdTo = createdTo ?? SqlDateTime.MaxValue.Value;
				userOrders = (from o in userOrders
							  where o.CreatedDateTime >= createdFrom && o.CreatedDateTime <= createdTo
							  select o);

				// price
				if (priceExclusive) {
					userOrders = (from o in userOrders
								  where o.OfferMoney > priceMin && o.OfferMoney < priceMax
								  select o);
				} else {
					userOrders = (from o in userOrders
								  where o.OfferMoney >= priceMin && o.OfferMoney <= priceMax
								  select o);
				}

				// offset
				userOrders = userOrders.Skip(offset);

				// limit
				if (limit > 0) {
					userOrders = userOrders.Take(limit);
				}

				// Return list of stuffs
				List<Object> returnObjs = new List<Object>();
				foreach (Order o in userOrders) {
					returnObjs.Add(SPHelper.JsonOrder(o));
				}
				if (returnObjs.Count == 0) returnObjs = null;

				return SuccessResult(returnObjs);
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // GetSentOrders

		/**
		 * Get an order belonging to a sepcific user account.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] GetOrder(
			string userId,						// current user id, used for authorization
			int orderId							// id of the order
		) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId;
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Double check
				UserAccount account = (from a in db.UserAccounts
									   where a.Id.Equals(parsedUserId)
									   select a).FirstOrDefault();
				if (account == null) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Retrieve the order
				Order userOrder = (from o in db.Orders
								 where o.Id == orderId
								 select o).FirstOrDefault();
				if (userOrder == null) {
					return FailureResult("Invalid order.", "invalid order");
				}

				// Check for ownership
				if (!userOrder.UserStuff.UserAccount.Id.Equals(parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				return SuccessResult(SPHelper.JsonOrder(userOrder));
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // GetOrder

		/**
		 * Update an order info.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] Update(
			string userId,						// current user id, used for authorization
			int orderId,						// id of the order

			int paymentType = int.MinValue,		// 0: Meet in person; 1: Transfer; 2: Postal; 3: Online
			decimal offerMoney = decimal.MinValue,	// offer price
			int status = int.MinValue,			// 0: Pending; 1: Accepted; 2: Denied
			string properties = null			// custom properties of the order
		) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId;
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Double check
				UserAccount account = (from a in db.UserAccounts
									   where a.Id.Equals(parsedUserId)
									   select a).FirstOrDefault();
				if (account == null) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Retrieve the order
				Order userOrder = (from o in db.Orders
								   where o.Id == orderId
								   select o).FirstOrDefault();
				if (userOrder == null) {
					return FailureResult("Invalid order.", "invalid order");
				}

				// Check for ownership
				if (!userOrder.UserStuff.UserAccount.Id.Equals(parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				if (paymentType != int.MinValue) {
					userOrder.PaymentType = paymentType;
				}
				if (offerMoney != decimal.MinValue) {
					userOrder.OfferMoney = offerMoney;
				}
				if (status != int.MinValue) {
					userOrder.Status = status;
				}
				if (properties != null) {
					userOrder.Properties = properties;
				}

				db.SubmitChanges();

				return SuccessResult(SPHelper.JsonOrder(userOrder));
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // Update

	}
}
